using System.Diagnostics;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    using sqlite3_callback = dxCallback;
    using sqlite3_stmt = Vdbe;

    public partial class Sqlite3
    {
        /*
    ** 2001 September 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** Main file for the SQLite library.  The routines in this file
    ** implement the programmer interface to the library.  Routines in
    ** other files are for internal use by SQLite and should not be
    ** accessed by users of the library.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/legacy_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */

        //#include "sqliteInt.h"

        /*
    ** Execute SQL code.  Return one of the SQLITE_ success/failure
    ** codes.  Also write an error message into memory obtained from
    ** malloc() and make pzErrMsg point to that message.
    **
    ** If the SQL is a query, then for each row in the query result
    ** the xCallback() function is called.  pArg becomes the first
    ** argument to xCallback().  If xCallback=NULL then no callback
    ** is invoked, even for queries.
    */
//C# Alias
        public static int exec(sqlite3 db, /* The database on which the SQL executes */    string zSql,
                               /* The SQL to be executed */    int NoCallback, int NoArgs, int NoErrors)
        {
            string Errors = "";
            return sqlite3_exec(db, zSql, null, null, ref Errors);
        }

        public static int exec(sqlite3 db, /* The database on which the SQL executes */    string zSql,
                               /* The SQL to be executed */    sqlite3_callback xCallback,
                               /* Invoke this callback routine */    object pArg, /* First argument to xCallback() */
                               int NoErrors)
        {
            string Errors = "";
            return sqlite3_exec(db, zSql, xCallback, pArg, ref Errors);
        }

        public static int exec(sqlite3 db, /* The database on which the SQL executes */    string zSql,
                               /* The SQL to be executed */    sqlite3_callback xCallback,
                               /* Invoke this callback routine */    object pArg, /* First argument to xCallback() */
                               ref string pzErrMsg /* Write error messages here */)
        {
            return sqlite3_exec(db, zSql, xCallback, pArg, ref pzErrMsg);
        }

        //OVERLOADS 
        public static int sqlite3_exec(
            sqlite3 db, /* The database on which the SQL executes */
            string zSql, /* The SQL to be executed */
            int NoCallback, int NoArgs, int NoErrors
            )
        {
            string Errors = "";
            return sqlite3_exec(db, zSql, null, null, ref Errors);
        }

        public static int sqlite3_exec(
            sqlite3 db, /* The database on which the SQL executes */
            string zSql, /* The SQL to be executed */
            sqlite3_callback xCallback, /* Invoke this callback routine */
            object pArg, /* First argument to xCallback() */
            int NoErrors
            )
        {
            string Errors = "";
            return sqlite3_exec(db, zSql, xCallback, pArg, ref Errors);
        }

        public static int sqlite3_exec(
            sqlite3 db, /* The database on which the SQL executes */
            string zSql, /* The SQL to be executed */
            sqlite3_callback xCallback, /* Invoke this callback routine */
            object pArg, /* First argument to xCallback() */
            ref string pzErrMsg /* Write error messages here */
            )
        {
            int rc = StatusCode.SQLITE_OK; /* Return code */
            string zLeftover = ""; /* Tail of unprocessed SQL */
            sqlite3_stmt pStmt = null; /* The current SQL statement */
            string[] azCols = null; /* Names of result columns */
            int nRetry = 0; /* Number of retry attempts */
            int callbackIsInit; /* True if callback data is initialized */

            if (!Utility.Sqlite3SafetyCheckOk(db)) return UnitTest.SQLITE_MISUSE_BKPT();

            if (zSql == null) zSql = "";

            MutexHelper.MutexEnter(db.mutex);
            Utility.Sqlite3Error(db, StatusCode.SQLITE_OK, 0);
            while ((rc == StatusCode.SQLITE_OK || (rc == StatusCode.SQLITE_SCHEMA && (++nRetry) < 2)) && zSql != "")
            {
                int nCol;
                string[] azVals = null;

                pStmt = null;
                rc = sqlite3_prepare(db, zSql, -1, ref pStmt, ref zLeftover);
                Debug.Assert(rc == StatusCode.SQLITE_OK || pStmt == null);
                if (rc != StatusCode.SQLITE_OK)
                {
                    continue;
                }
                if (pStmt == null)
                {
                    /* this happens for a comment or white-space */
                    zSql = zLeftover;
                    continue;
                }

                callbackIsInit = 0;
                nCol = sqlite3_column_count(pStmt);

                while (true)
                {
                    int i;
                    rc = sqlite3_step(pStmt);

                    /* Invoke the callback function if required */
                    if (xCallback != null && (StatusCode.SQLITE_ROW == rc ||
                                              (StatusCode.SQLITE_DONE == rc && callbackIsInit == 0
                                               && (db.flags & Flag.SQLITE_NullCallback) != 0)))
                    {
                        if (0 == callbackIsInit)
                        {
                            azCols = new string[nCol]; //Malloc.DbMallocZero(db, 2*nCol*sizeof(const char*) + 1);
                            //if ( azCols == null )
                            //{
                            //  goto exec_out;
                            //}
                            for (i = 0; i < nCol; i++)
                            {
                                azCols[i] = sqlite3_column_name(pStmt, i);
                                /* VdbeAux.VdbeSetColName() installs column names as UTF8
                ** strings so there is no way for sqlite3_column_name() to fail. */
                                Debug.Assert(azCols[i] != null);
                            }
                            callbackIsInit = 1;
                        }
                        if (rc == StatusCode.SQLITE_ROW)
                        {
                            azVals = new string[nCol]; // azCols[nCol];
                            for (i = 0; i < nCol; i++)
                            {
                                azVals[i] = sqlite3_column_text(pStmt, i);
                                if (azVals[i] == null && sqlite3_column_type(pStmt, i) != FundamentalDataType.SQLITE_NULL)
                                {
                                    //db.mallocFailed = 1;
                                    //goto exec_out;
                                }
                            }
                        }
                        if (xCallback(pArg, nCol, azVals, azCols) != 0)
                        {
                            rc = StatusCode.SQLITE_ABORT;
                            VdbeAux.VdbeFinalize(pStmt);
                            pStmt = null;
                            Utility.Sqlite3Error(db, StatusCode.SQLITE_ABORT, 0);
                            goto exec_out;
                        }
                    }

                    if (rc != StatusCode.SQLITE_ROW)
                    {
                        rc = VdbeAux.VdbeFinalize(pStmt);
                        pStmt = null;
                        if (rc != StatusCode.SQLITE_SCHEMA)
                        {
                            nRetry = 0;
                            if ((zSql = zLeftover) != "")
                            {
                                int zindex = 0;
                                while (zindex < zSql.Length && SqliteInt.Sqlite3Isspace(zSql[zindex])) zindex++;
                                if (zindex != 0) zSql = zindex < zSql.Length ? zSql.Substring(zindex) : "";
                            }
                        }
                        break;
                    }
                }

                MemPool.DbFree(db, ref azCols);
                azCols = null;
            }

            exec_out:
            if (pStmt != null) VdbeAux.VdbeFinalize(pStmt);
            MemPool.DbFree(db, ref azCols);

            rc = Malloc.ApiExit(db, rc);
            if (rc != StatusCode.SQLITE_OK && UnitTest.ALWAYS(rc == sqlite3_errcode(db)) && pzErrMsg != null)
            {
                //int nErrMsg = 1 + Utility.Sqlite3Strlen30(sqlite3_errmsg(db));
                //pzErrMsg = Malloc.sqlite3Malloc(nErrMsg);
                //if (pzErrMsg)
                //{
                //   memcpy(pzErrMsg, sqlite3_errmsg(db), nErrMsg);
                //}else{
                //rc = StatusCode.SQLITE_NOMEM;
                //Utility.Sqlite3Error(db, StatusCode.SQLITE_NOMEM, 0);
                //}
                pzErrMsg = sqlite3_errmsg(db);
            }
            else if (pzErrMsg != "")
            {
                pzErrMsg = "";
            }

            Debug.Assert((rc & db.errMask) == rc);
            MutexHelper.MutexLeave(db.mutex);
            return rc;
        }
    }
}